package com.uinnova.product.eam.service.es;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.binary.core.exception.MessageException;
import com.binary.core.util.BinaryUtils;
import com.binary.jdbc.Page;
import com.uinnova.product.vmdb.comm.model.ci.CCcCiClass;
import com.uinnova.product.vmdb.comm.model.ci.CcCiAttrDef;
import com.uinnova.product.vmdb.comm.util.PropertyType;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uino.bean.cmdb.base.*;
import com.uino.bean.cmdb.enums.AttrNameKeyEnum;
import com.uino.bean.cmdb.query.ESAttrAggBean;
import com.uino.bean.sys.base.ESCIOperateLog;
import com.uino.dao.AbstractESBaseDao;
import com.uino.dao.ESConst;
import com.uino.dao.cmdb.CiClassProDropSourceDefHelper;
import com.uino.dao.cmdb.ESCIAttrTransConfigSvc;
import com.uino.dao.cmdb.ESCIClassSvc;
import com.uino.dao.cmdb.ESCmdbCommSvc;
import com.uino.dao.sys.ESDictionaryItemSvc;
import com.uino.dao.util.ESUtil;
import com.uino.service.sys.microservice.ICIOperateLogSvc;
import com.uino.util.sys.CheckAttrUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * ES-CI分类服务
 * 
 * @author zoumengjie
 */
@Service
public class IamsEamESCIClassSvc extends AbstractESBaseDao<ESCIClassInfo, CCcCiClass> {

    Log logger = LogFactory.getLog(IamsEamESCIClassSvc.class);

    @Autowired
    private ESCIClassSvc classSvc;

    @Autowired
    @Lazy
    private ESCmdbCommSvc commSvc;

    @Autowired
    private IamsESCICommSvc esCiSvc;

    @Autowired
    private ESCIAttrTransConfigSvc attrConfigSvc;

    // @Autowired
    // private ESCIHistorySvc ciHistorySvc;

    @Autowired
    private ICIOperateLogSvc logSvc;

    @Autowired
    private ESDictionaryItemSvc dictSvc;

    private String defaultIcon = "/122/default_icon.png";

    public String getDefaultIcon() {
        return defaultIcon;
    }

    @Override
    public String getIndex() {
        return ESConst.INDEX_CMDB_CICLASS;
    }

    @Override
    public String getType() {
        return ESConst.INDEX_CMDB_CICLASS;
    }

    @Override
    public Long saveOrUpdate(ESCIClassInfo esClsInfo) {
        // 校验属性合法性
        if (!BinaryUtils.isEmpty(esClsInfo.getId())) {
            this.checkAttrDef(esClsInfo);
        }
        if (esClsInfo.getOrderNo() == null) {
            // 设置orderNo
            esClsInfo.setOrderNo(getNextOrderNo(esClsInfo.getDirId(), esClsInfo.getParentId()));
        }
        // 属性转换
        this.saveAttrTransConfigs(Collections.singletonList(esClsInfo));
        return super.saveOrUpdate(esClsInfo);
    }

    @Override
    public ESCIClassInfo getById(Long id) {
        ESCIClassInfo res = super.getById(id);
        if (res != null) {
            this.transAttrDefsToShowName(Collections.singletonList(res));
        }
        return res;
    }

    private void saveAttrTransConfigs(List<ESCIClassInfo> esClsInfos) {
        // Assert.notEmpty(esClsInfo.getCcAttrDefs(), "属性定义不能为空");
        Map<String, String> sourceAttrDefNames = new HashMap<>();
        // 查询CI mapping,防止与定义过的属性重复，属性类型更改，增加新字段、类型，需要在中间表里创建对应关系
        Map<String, Object> mapping = esCiSvc.getCIMapping();
        Map<String, Map<String, Object>> mappingMap = JSON.parseObject(JSON.toJSONString(mapping.get("properties")), new TypeReference<Map<String, Map<String, Object>>>() {
        });
        try {
            Object object = mappingMap.get("attrs").get("properties");
            Map<String, Map<String, String>> parseObject = JSON.parseObject(JSON.toJSONString(object), new TypeReference<Map<String, Map<String, String>>>() {
            });
            parseObject.forEach((key, value) -> {
                String symbol = value.get("type");
                sourceAttrDefNames.put(key.toUpperCase(), symbol);
            });
        } catch (Exception e) {
            logger.error("CI mapping 获取失败");
        }
        // 查询中间表数据，用于属性匹配
        Set<Long> classIds = esClsInfos.stream().filter(cls -> cls.getId() != null).map(ESCIClassInfo::getId).collect(Collectors.toSet());
        List<ESCIAttrTransConfig> attrsConfigs = attrConfigSvc.getListByQuery(QueryBuilders.termsQuery("classId", classIds));
        Map<Long, ESCIAttrTransConfig> attrConfigMap = BinaryUtils.toObjectMap(attrsConfigs, "defId");
        // 查询分类属性，重复需记录中间表
        Map<String, Set<Long>> sourseDefNameToClassIdMap = new HashMap<>();
        List<ESCIClassInfo> sourceClsDefs = classSvc.getSourceCIClassDefByQuery(QueryBuilders.boolQuery());
        Map<Long, ESCIClassInfo> clsMap = BinaryUtils.toObjectMap(sourceClsDefs, "id");
        for (ESCIClassInfo clsDef : sourceClsDefs) {
            for (CcCiAttrDef def : clsDef.getCcAttrDefs()) {
                sourceAttrDefNames.put(def.getProStdName(), this.getSymbolByProType(def.getProType()));
                if (!sourseDefNameToClassIdMap.containsKey(def.getProStdName())) {
                    sourseDefNameToClassIdMap.put(def.getProStdName(), new HashSet<>());
                }
                sourseDefNameToClassIdMap.get(def.getProStdName()).add(clsDef.getId());
                ESCIAttrTransConfig attrConfig = attrConfigMap.get(def.getId());
                if (!BinaryUtils.isEmpty(attrConfig)) {
                    // 通过中间表映射属性
                    if (!BinaryUtils.isEmpty(attrConfig)) {
                        def.setProName(attrConfig.getShowName());
                        def.setProStdName(attrConfig.getSourceAttrName().toUpperCase());
                        if (attrConfig.getUpType().intValue() > 1) {
                            def.setProStdName(attrConfig.getTargetAttrName());
                            def.setProType(attrConfig.getTargetAttrType());
                        }
                    }
                }
                // sourceAttrDefNames.add(def.getProStdName());
            }
        }

        for (ESCIClassInfo esClsInfo : esClsInfos) {
            List<ESCIAttrTransConfig> attrConfigs = new ArrayList<>();
            List<ESCIInfo> historys = new ArrayList<>();
            List<ESCIOperateLog> ciLogs = new ArrayList<>();
            Map<LibType, List<CcCiInfo>> sourceCIInfosMap = new HashMap<>();
            if (BinaryUtils.isEmpty(esClsInfo.getId())) {
                esClsInfo.setId(ESUtil.getUUID());
            }
            Map<Long, CcCiAttrDef> oldDefMap = new HashMap<>();
            ESCIClassInfo oldCls = clsMap.get(esClsInfo.getId());
            if (oldCls != null) {
                oldDefMap = BinaryUtils.toObjectMap(oldCls.getCcAttrDefs(), "id");
            } else {
                clsMap.put(esClsInfo.getId(), esClsInfo);
            }
            for (CcCiAttrDef def : esClsInfo.getCcAttrDefs()) {
                // 新增属性
                if (def.getId() == null) {
                    def.setId(ESUtil.getUUID());
                    String esSymbol = sourceAttrDefNames.get(def.getProStdName());
                    if (esSymbol != null && !esSymbol.equals(getSymbolByProType(def.getProType()))) {
                        // 同名，不同类型属性新建映射表
                        ESCIAttrTransConfig attrConfig = ESCIAttrTransConfig.builder().classId(esClsInfo.getId()).defId(def.getId()).sourceAttrName(def.getProName()).sourceAttrType(def.getProType())
                                .showName(def.getProName()).targetAttrName(ESUtil.getUUID() + "").targetAttrType(def.getProType()).upType(2).build();
                        attrConfigs.add(attrConfig);
                    } else {
                        // 同名，同类型、不同分类属性复用，不存映射表
                        Set<Long> sourceClsIds = sourseDefNameToClassIdMap.get(def.getProStdName());
                        if (!BinaryUtils.isEmpty(sourceClsIds) && sourceClsIds.contains(esClsInfo.getId())) {
                            ESCIAttrTransConfig attrConfig = ESCIAttrTransConfig.builder().classId(esClsInfo.getId()).defId(def.getId()).sourceAttrName(def.getProName())
                                    .sourceAttrType(def.getProType()).showName(def.getProName()).targetAttrName(ESUtil.getUUID() + "").targetAttrType(def.getProType()).upType(2).build();
                            attrConfigs.add(attrConfig);
                        }
                    }
                } else {
                    // 修改属性
                    CcCiAttrDef oldDef = oldDefMap.get(def.getId());
                    Assert.notNull(oldDef, "属性[" + def.getId() + "]不存在");
                    // 修改属性名称或类型记录到中间表
                    boolean isDefNameEqual = CheckAttrUtil.equalsForModel(oldDef.getProName(), def.getProName());
                    boolean isDefTypeEqual = CheckAttrUtil.equalsForModel(oldDef.getProType(), def.getProType());
                    if (!(isDefNameEqual && isDefTypeEqual)) {
                        ESCIAttrTransConfig attrConfig = attrConfigMap.get(def.getId());
                        if (BinaryUtils.isEmpty(attrConfig)) {
                            attrConfig = ESCIAttrTransConfig.builder().classId(esClsInfo.getId()).defId(def.getId()).sourceAttrName(oldDef.getProName()).sourceAttrType(oldDef.getProType())
                                    .showName(def.getProName()).build();
                        }
                        if (!isDefNameEqual) {
                            if (BinaryUtils.isEmpty(attrConfig.getUpType())) {
                                attrConfig.setUpType(1);
                            }
                            attrConfig.setShowName(def.getProName());
                        }
                        if (!isDefTypeEqual) {
                            QueryBuilder ciQuery = QueryBuilders.termQuery("classId", esClsInfo.getId());
                            if (sourceCIInfosMap.isEmpty()) {
                                esCiSvc.getLibTypes().forEach(libType -> sourceCIInfosMap.put(libType, esCiSvc.getCIInfosByQuery(ciQuery, false, libType)));
                            }
                            List<ESCIClassInfo> childCls = this
                                    .getListByQuery(QueryBuilders.termQuery("parentId", esClsInfo.getId()));
                            Set<Long> childIds = childCls.stream().filter(cls -> cls.getId() != null)
                                    .map(ESCIClassInfo::getId).collect(Collectors.toSet());
                            childIds.add(esClsInfo.getId());
                            String targetName = String.valueOf(ESUtil.getUUID());
                            String constraintRule = def.getConstraintRule();
                            if (def.getProType() == ESPropertyType.ENUM.getValue()) {
                                constraintRule = def.getEnumValues();
                            } else if (def.getProType() == ESPropertyType.DICT.getValue()) {
                                @SuppressWarnings("unchecked")
                                Long dictClassId = CiClassProDropSourceDefHelper.getCiClassProDropSourceClassId(def.getProDropSourceDef().trim());
                                Long[] dictDefIds = CiClassProDropSourceDefHelper.getCiClassProDropSourceDefIds(def.getProDropSourceDef().trim());
                                List<String> dictValues = dictSvc.getAttrValues(dictClassId, dictDefIds);
                                constraintRule = JSON.toJSONString(dictValues);
                            } else if (def.getProType() == ESPropertyType.EXTERNAL_ATTR.getValue()) {
                                @SuppressWarnings("unchecked")
                                Long ciClassId = CiClassProDropSourceDefHelper.getCiClassProDropSourceClassId(def.getProDropSourceDef().trim());
                                Long[] ciDefIds = CiClassProDropSourceDefHelper.getCiClassProDropSourceDefIds(def.getProDropSourceDef().trim());
                                ESCIClassInfo esciClassInfo = clsMap.get(ciClassId);
                                if (esciClassInfo != null) {
                                    Map<Long, String> attrMap = esciClassInfo.getAttrDefs().stream().collect(Collectors.toMap(ESCIAttrDefInfo::getId, ESCIAttrDefInfo::getProName));
                                    List<String> attrNames = new ArrayList<>();
                                    for (Long ciDefId : ciDefIds) {
                                        String proName = attrMap.get(ciDefId);
                                        Assert.notNull(proName, "属性不存在");
                                        attrNames.add(proName);
                                    }
                                    long countByCondition = esCiSvc.countByCondition(QueryBuilders.termQuery("classId", esciClassInfo.getId()), null);
                                    constraintRule = "[]";
                                    if (countByCondition != 0L) {
                                        Page<String> page = esCiSvc.queryAttrVal(esciClassInfo.getDomainId(), ciClassId, attrNames, false, new ESAttrAggBean());
                                        constraintRule = JSON.toJSONString(page.getData());
                                    }
                                } else {
                                    throw new MessageException("分类不存在");
                                }
                            }
                            if (attrConfig.getUpType() != null && attrConfig.getUpType().longValue() > 1) {
                                this.updateAttrValToNewAttr(childIds, attrConfig.getTargetAttrName(),
                                        attrConfig.getTargetAttrType(), targetName, def.getProType(), constraintRule,
                                        def.getDefVal());
                            } else {
                                this.updateAttrValToNewAttr(childIds, attrConfig.getSourceAttrName().toUpperCase(),
                                        attrConfig.getSourceAttrType(), targetName, def.getProType(), constraintRule,
                                        def.getDefVal());
                            }
                            attrConfig.setTargetAttrName(targetName);
                            attrConfig.setTargetAttrType(def.getProType());
                            attrConfig.setUpType(2);
                        }
                        // 不更改原属性名称，通过中间表匹配
                        def.setProName(attrConfig.getSourceAttrName());
                        def.setProStdName(attrConfig.getSourceAttrName().toUpperCase());
                        // 不更改原属性类型，通过中间表匹配
                        def.setProType(attrConfig.getSourceAttrType());
                        attrConfigs.add(attrConfig);
                    }
                }
                sourceAttrDefNames.put(def.getProName().toUpperCase(), this.getSymbolByProType(def.getProType()));
                // sourceAttrDefNames.add(def.getProName().toUpperCase());
            }
            // 先保存完属性映射，再保存历史版本，否则历史版本属性转换有问题
            if (!BinaryUtils.isEmpty(attrConfigs)) {
                attrConfigSvc.saveOrUpdateBatch(attrConfigs);
            }
            // 更改类型，记录历史版本与配置日志
            Iterator<LibType> it = sourceCIInfosMap.keySet().iterator();
            while (it.hasNext()) {
                LibType libType = it.next();
                List<CcCiInfo> sourceCIInfos = sourceCIInfosMap.get(libType);
                if (!BinaryUtils.isEmpty(sourceCIInfos)) {
                    QueryBuilder ciQuery = QueryBuilders.termQuery("classId", esClsInfo.getId());
                    List<ESCIInfo> esciInfos = esCiSvc.getESInfosByQuery(ciQuery, libType);
                    // long count = esCiSvc.countByCondition(ciQuery);
                    // List<ESCIInfo> esciInfos = esCiSvc.getListByQuery(1, new BigDecimal(count).intValue(),
                    // ciQuery).getData();
                    // List<CcCiInfo> targetCIInfos = esCiSvc.getCIInfoPageByQuery(1, new BigDecimal(count).intValue(),
                    // ciQuery, false).getData();
                    Map<Long, ESCIInfo> targetCIMap = BinaryUtils.toObjectMap(esciInfos, "id");
                    sourceCIInfos.forEach(sourceCiInfo -> {
                        ESCIInfo targetEsInfo = targetCIMap.get(sourceCiInfo.getCi().getId());
                        CcCiInfo targetCiInfo = commSvc.tranCcCiInfo(targetEsInfo, false);
                        if (!CheckAttrUtil.checkAttrMapEqual(sourceCiInfo.getAttrs(), targetCiInfo.getAttrs())) {
                            targetEsInfo.setVersion(targetEsInfo.getVersion() + 1L);
                            historys.add(targetEsInfo);
//                            ciLogs.add(CISvc.buildLogRecord(1L, SysUtil.StaticUtil.LOG_DYNAMIC_UPDATE, esClsInfo.getCcAttrDefs(), sourceCiInfo.getAttrs(), targetCiInfo.getAttrs(),
//                                esClsInfo.getClassName(), targetCiInfo.getCi()));
                        }
                    });
                    if (!BinaryUtils.isEmpty(historys)) {
                        esCiSvc.transCIAttrs(historys, false);
                        Set<Long> ids = historys.stream().map(ESCIInfo::getId).collect(Collectors.toSet());
                        esCiSvc.updateByQuery(QueryBuilders.termsQuery("id", ids), "ctx._source.version+=1", false, libType);
                        esCiSvc.saveOrUpdateHistoryInfosBatch(historys, ESCIHistoryInfo.ActionType.SAVE_OR_UPDATE, libType);
                    }
                    if (!BinaryUtils.isEmpty(ciLogs)) {
                        logSvc.saveOrUpdateBatch(ciLogs);
                    }
                }
            }
        }
    }

    /**
     * 将指定分类下原属性值复制到目标属性中，并按类型转换
     * 
     * @param classIds
     * @param sourceName
     * @param sourceType
     * @param targetName
     * @param targetType
     */
    private void updateAttrValToNewAttr(Set<Long> classIds, String sourceName, Integer sourceType, String targetName, Integer targetType, String constraintRule, String defValue) {
        String destAttrStr = "ctx._source.attrs['" + targetName + "']";
        String sourceAttrStr = "ctx._source.attrs['" + sourceName + "']";
        String sourceAttrVal = "String val=String.valueOf(" + sourceAttrStr + ");";
        if (sourceType.intValue() == PropertyType.DOUBLE.getValue()) {
            sourceAttrVal = "String val=new BigDecimal(" + sourceAttrStr + ".toString()).toString();";
        }
        if (sourceType.intValue() == PropertyType.DATE.getValue()) {
            sourceAttrVal = "String val=new SimpleDateFormat('" + CheckAttrUtil.DATE_FORMAT_DEFAULT + "').format(" + sourceAttrStr + ");";
        }
        String condition = null;
        PropertyType type = PropertyType.valueOf(targetType);
        switch (type) {
            case INTEGER:
                if (sourceType == ESPropertyType.DOUBLE.getValue()) {
                    condition = sourceAttrVal + destAttrStr
                            + "=Long.parseLong(val.lastIndexOf(\".\")!=-1?val.substring(0,val.lastIndexOf(\".\")):val)";
                } else {
                    condition = sourceAttrVal + "if(/" + CheckAttrUtil.INTEGER_REGEX.toString()
                            + "/.matcher(val).matches()){" + destAttrStr + "=Long.parseLong(val)}else{" + destAttrStr
                            + "=null}";
                }
                break;
            case DOUBLE:
                condition = sourceAttrVal + "if(/" + CheckAttrUtil.DOUBLE_REGEX2.toString() + "/.matcher(val).matches()){" + destAttrStr + "=new BigDecimal(val)}else{" + destAttrStr + "=null}";
                break;
            case INTERFACE:
                condition = destAttrStr + "=''";
                break;
            case ENCODE:
            case EXTERNAL_ATTR:
            case EXTERNAL_ASSET:
            case VARCHAR:
                condition = sourceAttrVal + destAttrStr + "=val.substring(0,val.length()<200?val.length():200)";
                break;
            case LONG_VARCHAR:
                condition = sourceAttrVal + destAttrStr + "=val.substring(0,val.length()<1000?val.length():1000)";
                break;
            case ATTACHMENT:
            case PERSION:
            case CLOB:
                // 此处不需要处理，直接转
                condition = sourceAttrVal + destAttrStr + "=val";
                break;
            case DATE:
                String dateAttrStr = "ctx._source.attrs['" + targetName + "_date']";
                condition = sourceAttrVal + "DateFormat format = new SimpleDateFormat('" + CheckAttrUtil.DATE_FORMAT_DEFAULT
                        + "');format.setLenient(false);DateFormat format2 = new SimpleDateFormat('yyyy-MM-dd');format2.setLenient(false);"
                        + destAttrStr + "=null;try{" + destAttrStr + "=format.parse(val).getTime();" + dateAttrStr
                        + "=val;}catch(Exception e){}if(" + destAttrStr + "==null){try{" + destAttrStr
                        + "=format2.parse(val).getTime();" + dateAttrStr + "=val;}catch(Exception e){}}";
                break;
            case ENUM:
            case DICT:
                condition = sourceAttrVal + "if(" + constraintRule + ".contains(val)){" + destAttrStr + "=val}else{"
                        + destAttrStr + "=null}";
                break;
            case PICTURE:
                condition = sourceAttrVal + destAttrStr + "=val";
                break;
            default:
                break;
        }
        String script = "if(" + sourceAttrStr + "!=null){" + condition + "}else{" + destAttrStr + "=null}";
        // 将原属性值复制到新属性中并做格式转换，并删除原属性
        esCiSvc.getLibTypes().forEach(libType -> {
            esCiSvc.updateByQuery(QueryBuilders.termsQuery("classId", classIds), script + "ctx._source.attrs.remove('" + sourceName + "')", true, libType);
            // 修改类型，把历史表数据一起刷了
            String historyScript = destAttrStr + "=" + sourceAttrStr + ";ctx._source.attrs.remove('" + sourceName + "')";
            if (sourceType.intValue() == PropertyType.DATE.getValue()) {
                String dateSourceAttrStr = "ctx._source.attrs['" + sourceName + "_date']";
                historyScript =
                        "if(" + dateSourceAttrStr + "!=null){" + destAttrStr + "=" + dateSourceAttrStr + "}else{" + destAttrStr + "=" + sourceAttrStr + "}ctx._source.attrs.remove('" + sourceName + "')";
            }
            esCiSvc.updateHistoryByQuery(QueryBuilders.termsQuery("classId", classIds), historyScript, true, libType);
        });
        // ciHistorySvc.updateByQuery(QueryBuilders.termsQuery("classId", classIds), historyScript, true);
    }

    /**
     * 校验属性合法性,若分类下已有数据，禁止修改属性
     *
     * @param esciClassInfo
     */
    private void checkAttrDef(ESCIClassInfo esciClassInfo) {
        // Assert.notEmpty(esciClassInfo.getCcAttrDefs(),
        // "BS_FIELD_EMPTY_VAL${\"field\":\"attrDefs\"}");
        Long clsId = esciClassInfo.getId();
        // 根据分类id查询ci的个数，包括子类
        List<ESCIClassInfo> childCls = this.getListByQuery(QueryBuilders.termQuery("parentId", clsId));
        Set<Long> classIds = childCls.stream().filter(cls -> cls.getId() != null).map(ESCIClassInfo::getId).collect(Collectors.toSet());
        classIds.add(clsId);
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.should(QueryBuilders.termsQuery("classId", classIds));
        long clsCiCount = esCiSvc.countByCondition(query, null);
        if (clsCiCount > 0) {
            ESCIClassInfo oldCls = this.getById(clsId);
            // 新属性模板信息
            List<CcCiAttrDef> newDefs = esciClassInfo.getCcAttrDefs();
            newDefs = newDefs == null ? new LinkedList<>() : newDefs;
            Map<Long, CcCiAttrDef> newDefMap = BinaryUtils.toObjectMap(newDefs, "id");
            // 旧的属性模板相关信息
            List<CcCiAttrDef> oldDefs = oldCls.getCcAttrDefs();
            oldDefs = oldDefs == null ? new LinkedList<>() : oldDefs;
            // List<String> changeMajorNames = new ArrayList<>();
            // 验证旧属性合法性
            for (CcCiAttrDef def : oldDefs) {
                CcCiAttrDef newDef = newDefMap.get(def.getId());
                if (newDef != null) {
                    boolean changeToMajor = def.getIsMajor() == 0 && newDef.getIsMajor() == 1;
                    Assert.isTrue(!changeToMajor, "该分类或其子类下已有数据不允许新增主键");
                    if (def.getIsMajor() == 1) {
                        Assert.isTrue(this.equalsAttrDef(newDef, def), "该分类或其子类下已有数据不允许修改主键");
                        Assert.isTrue(CheckAttrUtil.equalsForModel(newDef.getProType(), def.getProType()), "该分类或其子类下已有数据不允许修改主键类型");
                    }
                    boolean changeToRequired = def.getIsRequired() == 0 && newDef.getIsRequired() == 1;
                    if (changeToRequired) {
                        Assert.isTrue(this.getAttrMissingCount(clsId, def.getProName()) <= 0,
                                "[" + newDef.getProName() + "]属性值缺失，不可设置为必填");
                    }

                    //图片不能转普通属性
                    if (!def.getProType().equals(newDef.getProType()) && (def.getProType() == ESPropertyType.PICTURE.getValue() || newDef.getProType() == ESPropertyType.PICTURE.getValue())) {
                        Assert.isTrue(false, "[" + newDef.getProName() + "]属性类型不能转为" + AttrNameKeyEnum.valueOf(newDef.getProType()).getValue());
                    }
                    //文档不能转普通属性
                    if (!def.getProType().equals(newDef.getProType()) && (def.getProType() == ESPropertyType.DOCUMENT.getValue() || newDef.getProType() == ESPropertyType.DOCUMENT.getValue())) {
                        Assert.isTrue(false, "[" + newDef.getProName() + "]属性类型不能转为" + AttrNameKeyEnum.valueOf(newDef.getProType()).getValue());
                    }
                } else {
                    Assert.isTrue(def.getIsMajor() != 1, "分类下已有数据不允许删除主键");
                    // 清除CI属性值
                    esCiSvc.getLibTypes().forEach(libType -> esCiSvc.clearCIAttrByProName(clsId, def.getProName(), libType));
                }
            }
            List<CcCiAttrDef> validList = newDefs.stream().filter(
                    def -> def.getId() == null && ((def.getIsMajor() != null && def.getIsMajor() == 1)))
                    .collect(Collectors.toList());
            Assert.isTrue(validList.size() <= 0, "该分类或其子类下已有数据不允许新增主键");
        }
    }

    /**
     * 比较两个CI分类属性定义是否相同，用于修改分类时过滤未修改属性
     *
     * @param attrDef
     * @return
     */
    private boolean equalsAttrDef(CcCiAttrDef attrDef, CcCiAttrDef attrDef2) {
        if (attrDef == attrDef2) {
            return true;
        }
        return CheckAttrUtil.equalsForModel(attrDef.getId(), attrDef2.getId()) && CheckAttrUtil.equalsForModel(attrDef.getIsMajor(), attrDef2.getIsMajor())
                && CheckAttrUtil.equalsForModel(attrDef.getIsRequired(), attrDef2.getIsRequired()) && CheckAttrUtil.equalsForModel(attrDef.getEnumValues(), attrDef2.getEnumValues());
    }

    public long getAttrMissingCount(Long classId, String proName) {
        ESCIClassInfo classInfo = classSvc.getTargetAttrDefsByClassId(classId);
        for (CcCiAttrDef def : classInfo.getCcAttrDefs()) {
            if (def.getProName().equals(proName)) {
                List<ESCIClassInfo> childCls = this.getListByQuery(QueryBuilders.termQuery("parentId", classId));
                Set<Long> classIds = childCls.stream().filter(cls -> cls.getId() != null).map(ESCIClassInfo::getId).collect(Collectors.toSet());
                BoolQueryBuilder query = QueryBuilders.boolQuery();
                query.should(QueryBuilders.termsQuery("classId", classIds));
                query.mustNot(QueryBuilders.existsQuery("attrs." + def.getProStdName()));
                long count = esCiSvc.countByCondition(query, null);
                return count;
            }
        }
        return 0;
    }

    private String getSymbolByProType(Integer proType) {
        PropertyType type = PropertyType.valueOf(proType);
        switch (type) {
            case INTEGER:
                return "long";
            case DOUBLE:
                return "double";
            case DATE:
                return "long";
            default:
                return "text";
        }
    }

    /**
     * 获取下一个orderNo
     *
     * @param dirId
     * @param parentId
     * @author: weixuesong
     * @date: 2020/8/6 10:45
     * @return: int
     */
    public int getNextOrderNo(long dirId, long parentId) {
        BoolQueryBuilder countQuery = QueryBuilders.boolQuery();
        countQuery.must(QueryBuilders.termQuery("dirId", dirId));
        countQuery.must(QueryBuilders.termQuery("parentId", parentId));
        List<SortBuilder<?>> sorts = new LinkedList<>();
        sorts.add(SortBuilders.fieldSort("orderNo").order(SortOrder.DESC).unmappedType("long"));
        sorts.add(SortBuilders.fieldSort("createTime").order(SortOrder.DESC));
        Page<ESCIClassInfo> page = super.getSortListByQuery(1, 1, QueryBuilders.constantScoreQuery(countQuery), sorts);
        if (page.getData().isEmpty()) {
            return 0;
        }
        if (page.getData().get(0).getOrderNo() == null) {
            return 0;
        }
        return page.getData().get(0).getOrderNo() + 1;
    }

    public void transAttrDefsToShowName(List<ESCIClassInfo> classInfoList) {
        if (BinaryUtils.isEmpty(classInfoList)) {
            return;
        }
        Set<Long> clsIds = classInfoList.stream().map(ESCIClassInfo::getId).filter(Objects::nonNull).collect(Collectors.toSet());
        List<ESCIAttrTransConfig> attrConfigs = attrConfigSvc.getListByQuery(QueryBuilders.termsQuery("classId", clsIds));
        Map<Object, ESCIAttrTransConfig> attrConfigMap = BinaryUtils.toObjectMap(attrConfigs, "defId");
        for (ESCIClassInfo each : classInfoList) {
            Assert.notNull(each.getId(), "分类id不能为空");
            if (BinaryUtils.isEmpty(each.getAttrDefs())) {
                continue;
            }
            for (ESCIAttrDefInfo def : each.getAttrDefs()) {
                ESCIAttrTransConfig attrConfig = attrConfigMap.get(def.getId());
                def.setClassId(each.getId());
                // 通过中间表映射属性
                if (!BinaryUtils.isEmpty(attrConfig)) {
                    def.setProName(attrConfig.getShowName());
                    def.setProStdName(attrConfig.getShowName().toUpperCase());
                    if (attrConfig.getUpType() != 1) {
                        def.setProType(attrConfig.getTargetAttrType());
                    }
                }
                if(def.getProType().equals(150)&& StringUtils.isEmpty(def.getEnumValues())){
                    def.setEnumValues("0");
                }
            }
        }
    }

}
